module shader.litshadersun;

import render.render;
import scene.scene;
import scene.ray;
import scene.intersection;
import shader.abstractshader;
import shader.light;
import shader.lightsources;
import std.math;
import utils.color;
import utils.vector3;

const float SHADE_EPSILON = 1e-2f;

class LitShaderSun: AbstractShader {
    Light mSun;

    public this() {
        //default
        mSun = Light(Vector3f(1,1,1),RGBColor(1,1,0.7f));
        dirByHorizontal(PI/8,PI/6);
    }

    public void getColor(inout RenderContext context) {
        Vector3f x = context.rinters.hitpoint;
        Vector3f n = context.rinters.normal;
        Ray secRay;
        Intersection ints;
        //sunlight direction, constant over whole scene
        Vector3f wi = mSun.p.normal();
        float fDot = wi*n;
        if (fDot > 0) {
            //offset origin point a little into direction of surface
            //normal to avoid self-intersection
            secRay = Ray(x+n*SHADE_EPSILON,wi,float.max);
            bool v = !context.scene.intersectShadowRay(secRay);
            if (v) {
                //sunlight is independant of distance
                context.addDirectionalLight(mSun.col, wi);
            }
        }
    }

    public char[] toString() {
        return "Sunlight";
    }

    /**
     * Params:
     *  altitude = (Alt), sometimes referred to as elevation, that is the angle
     *             between the object and the observer's local horizon.
     *  azimuth = (Az), that is the angle of the object around the horizon
     *            (measured from the south point towards the west)
     */
    public void dirByHorizontal(float altitude, float azimuth) {
        mSun.p = Vector3f.fromSpherical(altitude-PI_2,azimuth+PI_2,1.0f);
    }

    /**
     * Params:
     *  day = days from 1st of january
     *  hour = hours from midnight
     *  latitude = latitude of observer
     */
    public void dirByTime(float day, float hour, float latitude = 0) {
        //declination
        float delta = -0.4092 * cos((2*PI)/365 * (day+10));
        //hour angle
        float h = (12-hour) * 0.2618;

        float sin_alt = sin(latitude)*sin(delta) + cos(latitude)*cos(delta)*cos(h);
        float cos_az_cos_alt = cos(latitude)*sin(delta) - sin(latitude)*cos(delta)*cos(h);
        float sin_az_cos_alt = -cos(delta)*sin(h);

        dirByHorizontal(asin(sin_alt),atan2(sin_az_cos_alt,cos_az_cos_alt));
    }

    public Light sun() {
        return mSun;
    }
    public void sun(Light s) {
        mSun = s;
    }
}
